﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetadataBasedConfig.EntityModel.StaticConstraint;
using System.Data.Objects;

namespace MetadataBasedConfig.EntityModel
{
    public static class EntityStaticConstraintExtension
    {
        public static IEnumerable<T> IncludeStaticConstraint<T>(this IEnumerable<T> enumerable)
        {
            StaticConstraintHelper.EnsureVerify<T>();
            List<string> allStaticConstaintPropertyNames = StaticConstraintHelper.GetStaticConstraintPropertyNames(typeof(T));

            if (allStaticConstaintPropertyNames.Count > 0)
            {
                if (typeof(ObjectQuery<T>).IsAssignableFrom(enumerable.GetType()))
                {
                    ObjectQuery<T> objectQuery = enumerable as ObjectQuery<T>;
                    StringBuilder predicateFilter = new StringBuilder();
                    List<ObjectParameter> objectParams = new List<ObjectParameter>();
                    foreach (var propertyName in allStaticConstaintPropertyNames)
                    {
                        if (predicateFilter.Length > 0)
                        {
                            predicateFilter.Append("AND ");
                        }
                        string paramName = StaticConstraintHelper.GenerateStaticConstraintParameterName(propertyName);

                        predicateFilter.AppendFormat("{0}.{1}=@{2} ", objectQuery.Name, propertyName, paramName);
                        objectParams.Add(new ObjectParameter(paramName, StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName)));
                    }
                    ObjectQuery<T> objectQuery2 = objectQuery.Where(predicateFilter.ToString());
                    objectParams.ForEach(p => objectQuery2.Parameters.Add(p));
                    return objectQuery2;
                }
                else
                {
                    return enumerable.ToList().Where(t =>
                    {
                        bool result = true;
                        foreach (var propertyName in allStaticConstaintPropertyNames)
                        {
                            result &= t.GetPropertyValue(propertyName) == StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName);
                        }
                        return result;
                    });
                }
            }

            return enumerable;
        }


        public static IEnumerable<T> IncludeStaticConstraint<T>(this IEnumerable<T> enumerable, string propertyName)
        {
            StaticConstraintHelper.EnsureVerify<T>();
            if (!StaticConstraintHelper.GetStaticConstraintPropertyNames(typeof(T)).Contains(propertyName))
            {
                throw new InvalidOperationException(string.Format("The property:{1} of type:{0} can't be static constrainable", typeof(T).FullName, propertyName));
            }

            if (typeof(ObjectQuery<T>).IsAssignableFrom(enumerable.GetType()))
            {
                string paramName = StaticConstraintHelper.GenerateStaticConstraintParameterName(propertyName);

                ObjectQuery<T> objectQuery = (ObjectQuery<T>)enumerable;
                StringBuilder predicateFilter = new StringBuilder();
                predicateFilter.AppendFormat("{0}.{1}=@{2} ", objectQuery.Name, propertyName, paramName);
                ObjectQuery<T> objectQuery2 = objectQuery.Where(predicateFilter.ToString());
                objectQuery2.Parameters.Add(new ObjectParameter(paramName, StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName)));
                return objectQuery2;
            }
            else
            {
                return enumerable.ToList().Where(t => t.GetPropertyValue(propertyName) == StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName));
            }
        }
    }
}
